package eerbaar.server.gamebuilder.game;

import java.io.IOException;
import java.util.List;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLStreamException;

import eerbaar.action.ActionFactory;
import eerbaar.action.IAction;
import eerbaar.context.IParams;
import eerbaar.context.ParamFactory;
import eerbaar.expression.ExpressionFactory;
import eerbaar.expression.IExpression;
import eerbaar.level.IActions;
import eerbaar.level.IComplete;
import eerbaar.level.IExpressionCollection;
import eerbaar.level.IExpressions;
import eerbaar.level.IHint;
import eerbaar.level.IHints;
import eerbaar.level.ILevel;
import eerbaar.level.IMessage;
import eerbaar.level.IMessages;
import eerbaar.level.IStartup;
import eerbaar.level.IState;
import eerbaar.level.IStates;
import eerbaar.level.LevelFactory;
import eerbaar.server.gamebuilder.config.ActionConfig;
import eerbaar.server.gamebuilder.config.ActionsConfig;
import eerbaar.server.gamebuilder.config.CompleteConfig;
import eerbaar.server.gamebuilder.config.ConfigLoader;
import eerbaar.server.gamebuilder.config.GameConfig;
import eerbaar.server.gamebuilder.config.HintConfig;
import eerbaar.server.gamebuilder.config.HintsConfig;
import eerbaar.server.gamebuilder.config.LevelConfig;
import eerbaar.server.gamebuilder.config.MessageConfig;
import eerbaar.server.gamebuilder.config.MessagesConfig;
import eerbaar.server.gamebuilder.config.PropConfig;
import eerbaar.server.gamebuilder.config.SetupConfig;
import eerbaar.server.gamebuilder.config.StateConfig;
import eerbaar.server.gamebuilder.config.StatesConfig;
import eerbaar.server.gamebuilder.config.WhenConfig;
import eerbaar.server.gamebuilder.config.WhensConfig;
import eerbaar.shared.exceptions.GameException;

public class LevelBuilder {

    public ILevel build(final int inLevelIndex) throws GameException {

        ILevel result = null;

        GameConfig config;
        try {
            config = new ConfigLoader().load();
        } catch (FactoryConfigurationError e) {
            throw new GameException(e);
        } catch (XMLStreamException e) {
            throw new GameException(e);
        } catch (IOException e) {
            throw new GameException(e);
        }

        List<LevelConfig> levels = config.getLevels().getLevels();
        for (LevelConfig levelConfig : levels) {

            int index = levelConfig.getIndex();
            if (index != inLevelIndex) {
                continue;
            }

            String goal = levelConfig.getGoal();

            ILevel level = LevelFactory.create(index, goal);
            result = level;

            createSetup(levelConfig, level);
            createMessages(levelConfig, level);
            createComplete(levelConfig, level);
            createStates(levelConfig, level);
            createHints(levelConfig, level);
        }

        return result;
    }

    private void createActions(IActions actionCol, List<ActionConfig> actions) {

        int priority = 0;
        for (ActionConfig actionConfig : actions) {

            String type = actionConfig.getType();
            IParams params = ParamFactory.createParams();
            List<PropConfig> propsList = actionConfig.getProps();
            for (PropConfig propConfig : propsList) {
                params.set(propConfig.getName(), propConfig.getValue());
            }

            IAction action = ActionFactory.createAction(type, params);
            if (action != null) {
                action.setPriority(++priority);
                actionCol.addAction(action);
            } else {
                System.out.println("action not found: " + type);
            }
        }
    }

    private void createComplete(LevelConfig levelConfig, ILevel level) {

        CompleteConfig completeConfig = levelConfig.getComplete();
        IComplete complete = level.getComplete();

        String next = completeConfig.getNext();
        complete.setNext(Integer.parseInt(next));

        List<WhenConfig> exps = completeConfig.getWhens();
        populateWhens(complete, exps);
    }

    private void createExpressions(IExpressions expCol, List<WhenConfig> whens) {

        for (WhenConfig whenConfig : whens) {

            String type = whenConfig.getType();
            IParams params = ParamFactory.createParams();
            List<PropConfig> propsList = whenConfig.getProps();
            for (PropConfig propConfig : propsList) {
                params.set(propConfig.getName(), propConfig.getValue());
            }

            IExpression expression = ExpressionFactory.createExpression(type,
                    params);
            if (expression != null) {
                expCol.addExpression(expression);
            } else {
                System.out.println("expression not found: " + type);
            }

            List<WhenConfig> childWhens = whenConfig.getWhens();
            createExpressions(expCol, childWhens);
        }
    }

    private void createHints(LevelConfig levelConfig, ILevel level) {

        HintsConfig hintsConfig = levelConfig.getHints();
        if (hintsConfig == null) {
            return;
        }

        IHints hints = level.getHints();

        List<HintConfig> hintConfigArray = hintsConfig.getHints();
        for (HintConfig hintConfig : hintConfigArray) {

            IHint hint = LevelFactory.createHint();
            hints.addHint(hint);

            hint.setMessage(hintConfig.getMessage());
        }

    }

    private void createMessages(LevelConfig levelConfig, ILevel level) {

        MessagesConfig messagesConfig = levelConfig.getMessages();
        IMessages messages = level.getMessages();

        List<MessageConfig> messageConfigArray = messagesConfig.getMessages();
        for (MessageConfig messageConfig : messageConfigArray) {

            IMessage message = LevelFactory.createMessage();
            messages.addMesssage(message);

            message.setName(messageConfig.getName());

            IActions actionCol = message;
            List<ActionConfig> actions = messageConfig.getActions();

            createActions(actionCol, actions);
        }

    }

    private void createSetup(LevelConfig levelConfig, ILevel level) {

        SetupConfig setupConfig = levelConfig.getSetup();
        IStartup startup = level.getStartup();

        List<ActionConfig> actions = setupConfig.getActions();
        IActions actionCol = startup;

        createActions(actionCol, actions);
    }

    private void createStates(LevelConfig levelConfig, ILevel level) {

        StatesConfig statesConfig = levelConfig.getStates();
        levelConfig.setStates(statesConfig);

        IStates states = level.getStates();

        int priority = 0;
        List<StateConfig> statesConfigArray = statesConfig.getStates();
        for (StateConfig stateConfig : statesConfigArray) {

            IState state = LevelFactory.createState();
            states.addState(state);

            state.setPriority(++priority);

            ActionsConfig actionsConfig = stateConfig.getActions();
            List<ActionConfig> actionList = actionsConfig.getActions();
            IActions actions = state.getActions();
            createActions(actions, actionList);

            WhensConfig whensConfig = stateConfig.getWhens();
            List<WhenConfig> whenList = whensConfig.getWhens();
            IExpressions expressions = state.getExpressions();
            createExpressions(expressions, whenList);
        }

    }

    private void populateWhens(IExpressionCollection expressionCollection,
            List<WhenConfig> exps) {

        for (WhenConfig expConfig : exps) {

            String type = expConfig.getType();
            IParams params = ParamFactory.createParams();
            List<PropConfig> propsList = expConfig.getProps();
            for (PropConfig propConfig : propsList) {
                params.set(propConfig.getName(), propConfig.getValue());
            }

            IExpression exp = ExpressionFactory.createExpression(type, params);
            if (exp != null) {
                expressionCollection.addExpression(exp);
            } else {
                assert (false) : "expression not found: " + type;
                System.out.println("expression not found: " + type);
            }

            List<WhenConfig> childExps = expConfig.getWhens();
            populateWhens2(exp, childExps);
        }
    }

    private void populateWhens2(IExpression expressionCollection,
            List<WhenConfig> exps) {

        for (WhenConfig expConfig : exps) {

            String type = expConfig.getType();
            IParams params = ParamFactory.createParams();
            List<PropConfig> propsList = expConfig.getProps();
            for (PropConfig propConfig : propsList) {
                params.set(propConfig.getName(), propConfig.getValue());
            }

            IExpression exp = ExpressionFactory.createExpression(type, params);
            if (exp != null) {
                expressionCollection.addExpression(exp);
            } else {
                assert (false) : "expression not found: " + type;
                System.out.println("expression not found: " + type);
            }

            List<WhenConfig> childExps = expConfig.getWhens();
            populateWhens2(exp, childExps);
        }
    }
}
